<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type" />
<link rel="STYLESHEET" href="filtersdk.css" type="text/css" /><title>InvertNeg - AviSynth</title></head>
<body>
<h2><span style="text-decoration: none;">InvertNeg</span></h2>
<p>I'll start off with a complete, working Avisynth plugin. It's
called "InvertNeg", and it produces a photo-negative of the input clip.
</p>
<p>Here's InvertNeg.cpp:
</p>
<pre>#include &lt;windows.h&gt;<br />#include "avisynth.h"<br /><br />class InvertNeg&nbsp;: public GenericVideoFilter {<br />public:<br />   InvertNeg(PClip _child, IScriptEnvironment* env);<br />   PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);<br />};<br /><br />InvertNeg::InvertNeg(PClip _child, IScriptEnvironment* env)&nbsp;:<br />   GenericVideoFilter(_child) {<br />   if (!vi.IsPlanar() ||&nbsp;!vi.IsYUV()) {<br />      env-&gt;ThrowError("InvertNeg: planar YUV data only!");<br />   }<br />}<br /><br />PVideoFrame __stdcall InvertNeg::GetFrame(int n, IScriptEnvironment* env) {<br /><br />   PVideoFrame src = child-&gt;GetFrame(n, env);<br />   PVideoFrame dst = env-&gt;NewVideoFrame(vi);<br /><br />   const unsigned char* srcp;<br />   unsigned char* dstp;<br />   int src_pitch, dst_pitch, row_size, height;<br />   int p, x, y;<br /><br />   int planes[] = {PLANAR_Y, PLANAR_V, PLANAR_U};<br />	<br />   for (p=0; p&lt;3; p++) {<br />      srcp = src-&gt;GetReadPtr(planes[p]);<br />      dstp = dst-&gt;GetWritePtr(planes[p]);<br /><br />      src_pitch = src-&gt;GetPitch(planes[p]);<br />      dst_pitch = dst-&gt;GetPitch(planes[p]);<br />      row_size = dst-&gt;GetRowSize(planes[p]);<br />      height = dst-&gt;GetHeight(planes[p]);<br /><br />      for (y = 0; y &lt; height; y++) {<br />         for (x = 0; x &lt; row_size; x++) {<br />            dstp[x] = srcp[x] ^ 255;<br />         }<br />         srcp += src_pitch; // or srcp = srcp + src_pitch;<br />         dstp += dst_pitch; // or dstp = dstp + dst_pitch;<br />      }<br />   }<br />   return dst;<br />}<br /><br />AVSValue __cdecl Create_InvertNeg(AVSValue args, void* user_data, IScriptEnvironment* env) {<br />   return new InvertNeg(args[0].AsClip(), env);<br />}<br /><br />const AVS_Linkage *AVS_linkage = 0;<br /><br />extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) {<br />   AVS_linkage = vectors;<br />   env-&gt;AddFunction("InvertNeg", "c", Create_InvertNeg, 0);<br />   return "InvertNeg sample plugin";<br />}</pre>
<p>Compile this file into a DLL named InvertNeg.dll. See <a href="CompilingAvisynthPlugins.htm" title="Filter SDK/Compiling instructions">compiling
instructions</a>. Now create an Avisynth script which looks
something like this:
</p>
<pre>LoadPlugin("d:\path\InvertNeg.dll")<br />clip = BlankClip().ConvertToYV12()<br />return clip.InvertNeg()<br /></pre>
<h3><span class="editsection"></span><span class="mw-headline" id="Line_by_line_breakdown">&nbsp;Line
by line breakdown </span></h3>
<p>Here's a line-by-line breakdown of InvertNeg.cpp.
</p>
<pre>#include "avisynth.h"<br /></pre>
<p>This header declares all the classes and miscellaneous
constants that you might need when writing a plugin. All external
plugins should #include it.
</p>
<p>External plugins do not link with avisynth.dll, so they can't
directly access functions that are defined in the main Avisynth source
code. Therefore, every important function in avisynth.h is either
defined inline or declared as virtual. The virtual functions act as
callbacks for external DLLs.
</p>
<pre>class InvertNeg&nbsp;: public GenericVideoFilter {<br /></pre>
<p>An Avisynth filter is simply a C++ class implementing the <a href="Cplusplus_api.htm#IClip" title="Cplusplus API">IClip</a> interface. More about
classes can be found in this <a rel="nofollow" class="external text" href="http://www.cplusplus.com/doc/tutorial/classes">tutorial</a>.
IClip has four pure virtual methods: <a href="Cplusplus_api.htm#GetVideoInfo" title="Cplusplus API">GetVideoInfo</a>, <a href="Cplusplus_api.htm#GetFrame" title="Cplusplus API">GetFrame</a>, <a href="Cplusplus_api.htm#GetParity" title="Cplusplus API">GetParity</a>, and <a href="Cplusplus_api.htm#GetAudio" title="Cplusplus API">GetAudio</a>.
</p>
<p>The class GenericVideoFilter is a simple do-nothing filter
defined in avisynth.h. It derives from IClip and implements all four
methods. Most filters can inherit from GenericVideoFilter rather than
directly from IClip; this saves you from having to implement methods
that you don't care about, like GetAudio in this example. More about
inheritance can be found in this <a rel="nofollow" class="external text" href="http://www.cplusplus.com/doc/tutorial/inheritance">tutorial</a>.
In our case two functions are defined in the class: the constructor
(InvertNeg()) and GetFrame.
</p>
<pre>InvertNeg(PClip _child, IScriptEnvironment* env);<br /></pre>
<p>This is our constructor. It can be used to initialize
variables, create look up tables (LUTs) and to check whether the source
clips (if there is one) have the desired properties. In our example it
is taking a single clip, does some property checking and then simply
passes through to its output.
</p>
<p>A PClip is a "smart pointer" to an IClip. It maintains a
reference count on the IClip object and automagically deletes it when
the last PClip referencing it goes away. For obvious reasons, you
should always use PClip rather than IClip* to refer to clips.
</p>
<p>Like a genuine pointer, a PClip is only four bytes long, so
you can pass it around by value. Also like a pointer, a PClip can be
assigned a null value (0), which is often useful as a sentinel. Unlike
a pointer, PClip is initialized to 0 by default.
</p>
<p>You'll need to make sure your class doesn't contain any
circular PClip references, or any PClips sitting in dynamically
allocated memory that you forget to delete. Other than that, you don't
have to worry about the reference-counting machinery.
</p>
<p>Avisynth filters have a standardized output channel via IClip,
but (unlike for example <a href="http://avisynth.nl/index.php/VirtualDub" title="VirtualDub">VirtualDub</a> filters) no
standardized input channel. Each filter is responsible for obtaining
its own source material -- usually (as in this case) from another clip,
but sometimes from several different clips, or from a file.
</p>
<p>We will override the GetFrame method to do something more
useful, while leaving the other three methods as-is to pass through
aspects of the clip that we don't need to change.
</p>
<pre>PVideoFrame InvertNeg::GetFrame(int n, IScriptEnvironment* env) {<br /></pre>
<p>This method is called to make our filter produce frame n of
its output. The second argument, env, is for our purposes simply a
callback suite. It is actually implemented in Avisynth by a class
called ScriptEnvironment. One <a href="Cplusplus_api.htm#IScriptEnvironment" title="Cplusplus API">instance of this class</a> is
created for each opened AVS script, so there may sometimes be several
instances active at once. It is important that the callback functions
be called through the proper instance. A particular instance of your
class will only be used in one ScriptEnvironment, but different
instances might be used in different ScriptEnvironments.
</p>
<pre>InvertNeg::InvertNeg(PClip _child, IScriptEnvironment* env)&nbsp;:<br />   GenericVideoFilter(_child) {</pre>
<p>This is the class constructor and it is called when a filter
instance is being created (see Create_InvertNeg()).
</p>
<pre>  if (!vi.IsPlanar() ||&nbsp;!vi.IsYUV()) {<br />     env-&gt;ThrowError("InvertNeg: planar YUV data only!");<br />  }</pre>
<p>"vi" is a protected member of GenericVideoFilter. It is a
structure of type <a href="VideoInfo.htm" title="Cplusplus API/VideoInfo">VideoInfo</a>, and it
contains information about the clip (like frame size, frame rate, pixel
format, audio sample rate, etc.). In our example, when the image format
of the clip is not planar or the colorspace of the clip is not YUV, an
error is returned using <a href="Cplusplus_api.htm#ThrowError" title="Cplusplus API">ThrowError</a>. As of writing
this document there is no planar RGB, but that might very well change
in the future.
</p>
<pre> PVideoFrame src = child-&gt;GetFrame(n, env);<br /></pre>
<p>This method returns a PVideoFrame, which is a smart pointer
like PClip.
</p>
<p>"child" is the other protected member of GenericVideoFilter,
of type PClip. It contains the clip that was passed to the constructor.
For our filter to produce frame n we need the corresponding frame of
the input. If you need a different frame from the input, all you have
to do is pass a different frame number to child-&gt;GetFrame.
</p>
<p>GetFrame calls are usually intercepted by Avisynth's internal
caching code, so the frame request may never actually reach the child
filter.
</p>
<pre> PVideoFrame dst = env-&gt;NewVideoFrame(vi);<br /></pre>
<p>The NewVideoFrame callback allocates space for a video frame
of the supplied size. In this case it will hold our filter's output.
The frame buffer is uninitialized raw memory (except that in the debug
build it gets filled with the repeating byte pattern 0A 11 0C A7 ED,
which is easy to recognize because it looks like "ALLOCATED").
</p>
<p>As already explained, "vi" contains information about the
properties of the clip. NewVideoFrame uses the information in this
structure to return a frame buffer of the appropriate size.
</p>
<p>Frame buffers are reused once all the PVideoFrame references
to them go away. So usually NewVideoFrame won't actually need to
allocate any memory from the heap.
</p>
<pre> const unsigned char* srcp;<br /> unsigned char* dstp;<br /> int src_pitch, dst_pitch, row_size, height;<br /> int p, x, y;<br /><br /> int planes[] = {PLANAR_Y, PLANAR_V, PLANAR_U};<br /></pre>
<p>This is the declaration of the used variables. Note that we
will modify one color component (instead of an entire pixel) at the
same time. Such a component (for example an U sample) has a value
between 0 and 255 (exactly one byte). Hence srcp and dstp needs to be
declared as unsigned char (or BYTE would have been possible too).
</p>
<p>Note that in the constructor we excluded the formats which are
not planar or not YUV. For filter writers this means that they can
write one simple function that is called three times, one for each
color channel, assuming that the operations are channel-independent
(which is not always the case). This is what I have done here. Note it
also works for Y8 since these functions return zero if the plane
(PLANAR_U, PLANAR_V) doesn't exist. In our case the operations are
channel-independent so it doesn't matter which plane is processed
first.
</p>
<pre>   for (p=0; p&lt;3; p++) {<br />      srcp = src-&gt;GetReadPtr(planes[p]);<br />      dstp = dst-&gt;GetWritePtr(planes[p]);</pre>
<p>All frame buffers are readable, but not all are writable.
</p>
<p>The rule about writability is this: A buffer is writable if
and only if there is exactly one PVideoFrame pointing to it. In other
words, you can only write to a buffer if no one else might be reading
it. This rule guarantees that as long as you hold on to a PVideoFrame
and don't write to it yourself, that frame will remain unchanged. The
only drawback is that you can't have two PVideoFrames pointing to a
writable buffer. This can sometimes be an inconvenience, as I'll
explain later.
</p>
<p>Any buffer you get from NewVideoFrame is guaranteed to be
writable (as long as you only assign it to one PVideoFrame!). Our
filter's dst came from NewVideoFrame, so we can safely call
dst-&gt;GetWritePtr(). However, frames you get from other clips via
GetFrame may not be writable, in which case GetWritePtr() will return a
null pointer.
</p>
<p>There is an <a href="Cplusplus_api.htm#IsWritable" title="Cplusplus API">IsWritable</a>() method which
you can call to find out if a buffer is writable or not, and there's a <a href="Cplusplus_api.htm#MakeWritable" title="Cplusplus API">MakeWritable</a> callback to
ensure that it is.
</p>
<pre> src_pitch = src-&gt;GetPitch(planes[p]);<br /> dst_pitch = dst-&gt;GetPitch(planes[p]);<br /></pre>
<p>Just as in VirtualDub, the "pitch" of a frame buffer is the
offset (in bytes) from the beginning of one scan line to the beginning
of the next. The source and destination buffers won't necessarily have
the same pitch.
</p>
<p>Buffers created by NewVideoFrame are always quadword (8-byte)
aligned and always have a pitch that is a multiple of 8. (<span style="color: red;">i think it's
16 now, need to check&nbsp;???</span>)
</p>
<pre> row_size = dst-&gt;GetRowSize(planes[p]);<br /></pre>
<p>The row size is the length of each row in bytes (not pixels)
of plane p. It's usually equal to the pitch or slightly less, but it
may be significantly less if the frame in question has been through
Crop.
</p>
<p>Since our source and destination frames have the same width
and pixel format, they will always have the same row size. Thus I only
need one row_size variable, and I could just as well have called
src-&gt;GetRowSize().
</p>
<pre>height = dst-&gt;GetHeight(planes[p]);<br /></pre>
<p>The height is the height in samples of the plane p. Again, for
our filter this is the same for the source and the destination.
</p>
<pre>     for (y = 0; y &lt; height; y++) {<br />        for (x = 0; x &lt; row_size; x++) {<br />           dstp[x] = srcp[x] ^ 255;<br />        }<br />        srcp += src_pitch;<br />        dstp += dst_pitch;<br />     }</pre>
<p>This is the code that does the actual work. The "srcp +=
src_pitch; dstp += dst_pitch;" idiom is a useful way of dealing with
potentially differing pitches without too much grief.
</p>
<pre> return dst;<br /></pre>
<p>GetFrame returns the newly-created frame. Our own references
to this frame and to the source frame will go away with the src and dst
variables. Our caller will become sole owner of the destination frame
(which therefore will still be writable), and the source frame will be
retained in the cache and eventually recycled. All through the magic of
C++ classes.
</p>
<pre>AVSValue __cdecl Create_InvertNeg(AVSValue args, void* user_data, IScriptEnvironment* env) {<br /></pre>
<p>In order to use our new filter, we need a scripting-language
function which creates an instance of it. This is that function.
</p>
<p>Script functions written in C++ take three arguments. args
contains all the arguments passed to the function by the script.
user_data contains the void pointer which you passed to AddFunction
(see below). Usually you won't need this. env contains the same
IScriptEnvironment pointer that will later be passed to GetFrame.
</p>
<p>AVSValue is a variant type which can hold any one of the
following: a boolean value (true/false); an integer; a floating-point
number; a string; a video clip (PClip); an array of AVSValues; or
nothing ("undefined"). You can test which one it is with the methods
IsBool(), IsInt(), IsFloat(), IsString(), IsClip(), IsArray(), and
Defined() (which returns true if the AVSValue is not "undefined"). You
can get the value with AsBool(), AsInt(), etc. For arrays, you can use
the ArraySize() method to get the number of elements, and [] indexing
to get the elements themselves. For convenience, IsFloat() and
AsFloat() will work with integers also. But boolean values are not
treated as numeric (unlike C).
</p>
<p>The name "Create_InvertNeg" is arbitrary. This function will
actually be known as "InvertNeg" in scripts, because that's the name we
pass to <a href="Cplusplus_api.htm#AddFunction" title="Cplusplus API">AddFunction</a> below.
</p>
<pre> return new InvertNeg(args[0].AsClip(), env);<br /></pre>
<p>The args argument passed to a script function will always be
an array. The return value should be any one of the other types (never
an array).
</p>
<p>The types of the values in the args array are guaranteed to
match one of the function signatures that you pass to AddFunction, just
as in VirtualDub. Therefore, there's no need to worry about IsClip
here.
</p>
<p>Create_InvertNeg simply creates and returns a filter instance;
it is automatically converted to an AVSValue via the constructor
AVSValue(IClip*).
</p>
<pre>const AVS_Linkage *AVS_linkage = 0;<br /></pre>
<p>This declares and initializes the server pointers static
storage <a href="AVSLinkage.html" title="Filter SDK/AVS Linkage">AVS_Linkage</a>.
</p>
<pre>extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) {<br /></pre>
<p>This is the only function which gets exported from the DLL. It
is called by the script function LoadPlugin the first time this plugin
in loaded in a particular script. If several scripts are open at once
and more than one of them loads this plugin, AvisynthPluginInit3 may be
called more than once with different IScriptEnvironments. Therefore:
</p>
<ul>
<li> You should not save the env parameter in a global
variable.
</li>
<li> If you need to initialize any static data, you should do
it in DLLMain, not in this function. </li>
</ul>
<pre> AVS_linkage = vectors;<br /></pre>
<p>This saves the server pointers.
</p>
<p>The main purpose of the AvisynthPluginInit3 function is to
call env-&gt;AddFunction.
</p>
<pre> env-&gt;AddFunction("InvertNeg", "c", Create_InvertNeg, 0);<br /></pre>
<p>As promised, we now call AddFunction to let Avisynth know of
the existence of our filter. This function takes four arguments: the
name of the new script function; the parameter-type string; the C++
function implementing the script function; and the user_data cookie.
</p>
<p>The parameter-type string can be specified as follows:
</p>
<ul>
<li> No return type is given. Function return values are not
type-checked; you can return anything you like.
</li>
<li> The following types are available:
 <ul>
  <li> c - clip</li>
  <li> i - integer</li>
  <li> f - float</li>
  <li> s - string</li>
  <li> b - boolean</li>
 </ul>
</li>
<li> You can follow any type with '*' or '+' to indicate "zero
or more" or "one or more" respectively. In this case all the matching
arguments will be gathered into a sub-array. </li>
</ul>
<dl>
<dd>
For example, if your type string is "is+f", then the
integer argument will be args[0], the string arguments will be
args[1][0], args[1][1], etc. (and there will be args[1].ArraySize() of
them), and the float argument will be args[2].
</dd>
</dl>
<ul>
<li> '.' matches a single argument of any type. To match
multiple arguments of any type, use ".*" or ".+".
</li>
<li> You can have named arguments, by specifying the name in
[brackets] before the type. Named arguments are also optional
arguments; if the user omits them, they will be of the undefined type
instead of the type you specify. For convenience, AVSValue offers a set
of As...() functions which take default values. See avisynth.h. </li>
</ul>
<pre> return "InvertNeg sample plugin";<br /></pre>
<p>The return value of AvisynthPluginInit3 is a string which can
contain any message you like, such as a notice identifying the version
and author of the plugin. This string becomes the return value of
LoadPlugin, and will almost always be ignored. You can also just return
0 if you prefer.
</p>
<h3><span class="editsection"></span><span class="mw-headline" id="A_variation_as_an_in_place_filter">&nbsp;A
variation as an in place filter </span></h3>
<p>The Invert filter could easily do its work in a single buffer,
rather than copying from one buffer to another. Here's a new
implementation of GetFrame that does this.
</p>
<pre>#include &lt;windows.h&gt;<br />#include "avisynth.h"<br /><br />class InvertNeg&nbsp;: public GenericVideoFilter {<br />public:<br />   InvertNeg(PClip _child, IScriptEnvironment* env);<br />   PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);<br />};<br /><br />InvertNeg::InvertNeg(PClip _child, IScriptEnvironment* env)&nbsp;:<br />   GenericVideoFilter(_child) {<br />   if (!vi.IsPlanar() ||&nbsp;!vi.IsYUV()) {<br />      env-&gt;ThrowError("InvertNeg: planar YUV data only!");<br />   }<br />}<br /><br />PVideoFrame __stdcall InvertNeg::GetFrame(int n, IScriptEnvironment* env) {<br /><br />   PVideoFrame src = child-&gt;GetFrame(n, env);<br />   env-&gt;MakeWritable(&amp;src);<br /><br />   unsigned char* srcp;<br />   int src_pitch, row_size, height;<br />   int p, x, y;<br /><br />   int planes[] = {PLANAR_Y, PLANAR_V, PLANAR_U};<br />	<br />   for (p=0; p&lt;3; p++) {<br />      srcp = src-&gt;GetWritePtr(planes[p]);<br /><br />      src_pitch = src-&gt;GetPitch(planes[p]);<br />      row_size = src-&gt;GetRowSize(planes[p]);<br />      height = src-&gt;GetHeight(planes[p]);<br /><br />      for (y = 0; y &lt; height; y++) {<br />         for (x = 0; x &lt; row_size; x++) {<br />            srcp[x] = srcp[x] ^ 255;<br />            // even shorter would be srcp[x] ^= 255;<br />         }<br />         srcp += src_pitch;<br />      }<br />   }<br />   return src;<br />}<br /><br />AVSValue __cdecl Create_InvertNeg(AVSValue args, void* user_data, IScriptEnvironment* env) {<br />   return new InvertNeg(args[0].AsClip(), env);<br />}<br /><br />const AVS_Linkage *AVS_linkage = 0;<br /><br />extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) {<br />   AVS_linkage = vectors;<br />   env-&gt;AddFunction("InvertNeg", "c", Create_InvertNeg, 0);<br />   return "InvertNeg sample plugin";<br />}</pre>
<p>The key difference between this version of the function and
the original version is the presence of the <a href="Cplusplus_api.htm#MakeWritable" title="Cplusplus API">MakeWritable</a> callback. This
is necessary because this time "we don't know where that source frame
has been." Someone else in the filter chain may be holding a reference
to it, in which case we won't be allowed to write to it.
</p>
<h3><span class="editsection"></span><span class="mw-headline" id="Old_versions">&nbsp;Old
versions </span></h3>
<p><a href="http://avisynth.nl/index.php/Filter_SDK/Ben_docs" title="Filter SDK/Ben docs">Ben's AviSynth Docs</a> is
the documentation written for AviSynth 1.0 by Ben Rudiak-Gould, in its
original form.
</p>
<p>See more about the modifications for AviSynth 2.5 in the <a href="http://avisynth.nl/index.php/Filter_SDK/Two-Five_SDK" title="Filter SDK/Two-Five SDK">AviSynth Two-Five SDK</a>.
(need to adjust the two-five version using the code above ...)</p>
<hr style="width: 100%; height: 2px;" />Back to&nbsp;<a href="FilterSDK.htm">FilterSDK</a><br />
<p><kbd>$Date: 2015/09/14 20:23:59 $</kbd></p>
</body></html>